package com.sunda.spmswms.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunda.spmscommon.ResponseResult;
import com.sunda.spmscommon.feign.IFeignServiceCommon;
import com.sunda.spmswms.entity.InventoryCheckAction;
import com.sunda.spmswms.entity.WhsStorageInventory;
import com.sunda.spmswms.mapper.InventoryCheckActionMapper;
import com.sunda.spmswms.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.*;

import static com.sunda.spmscommon.Constans.*;

/**
 * <p>
 * 仓库盘盈盘亏 服务实现类
 * </p>
 *
 * @author Wayne
 * @since 2021-05-10
 */
@Service
public class InventoryCheckActionServiceImpl extends ServiceImpl<InventoryCheckActionMapper, InventoryCheckAction> implements IInventoryCheckActionService {

    @Autowired
    IInventoryCheckActionDtlService iInventoryCheckActionDtlService;

    @Autowired
    IFeignServiceCommon iFeignServiceCommon;

    @Autowired
    ISapDeliveryNoteLogService iSapDeliveryNoteLogService;

    @Autowired
    IWhsStorageInventoryService iWhsStorageInventoryService;

    @Autowired
    IWhsInventoryInfoService iWhsInventoryInfoService;

    @Override
    public List<InventoryCheckAction> getInvChkAcByInvChkId(String invChkId) {
        return this.baseMapper.selectList(new QueryWrapper<InventoryCheckAction>().eq("INVENTORY_CHECK_ID",invChkId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult getInvCheckAcWithDtl(String uuid) {
        try {
            QueryWrapper<InventoryCheckAction> queryWrapper = new QueryWrapper<InventoryCheckAction>().eq("UUID", uuid);
            InventoryCheckAction inventoryCheckAction = this.baseMapper.selectOne(queryWrapper);
            if (null == inventoryCheckAction) {
                throw new Exception("Could not find Inventory Check Action for uuid: " + uuid);
            }

            ResponseResult res = iInventoryCheckActionDtlService.getInvCheckAcDtl(uuid);
            if (res.getCode() != 200) {
                throw new Exception(res.getMsg());
            }

            return res.add("invCheckAc", inventoryCheckAction);

        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error(e.getMessage());
        }
    }


    /** 获取库存盘点任务列表
    *{
          "werks":["CN01","CM31"],
          "whsLocationCode":["1011","1061"],
          "inventoryCheckType":"X",  // X-备件货, M-贸易货
          "inventoryCheckCategory":"1", //盘点类别 1-日常盘点 2-月度盘点 3-动态盘点 4-其它盘点
          "createDate":["20210401","20210501"],
          "inventoryCheckId":"CN01-20210511-INV",
          "spmsStatus":"1", //任务状态：1-待盘点；2-盘点中；3-复盘中；4-处理中；5-关闭(完成)
      }
    * */
    @Override
    public ResponseResult getInvCheckAcList(JSONObject InvCheckFilter) {
        List<String> werks = InvCheckFilter.containsKey("werks") ? (List) InvCheckFilter.getJSONArray("werks") : null;
        List<String> whsLocationCode = InvCheckFilter.containsKey("whsLocationCode") ? (List) InvCheckFilter.getJSONArray("whsLocationCode") : null;
        String inventoryCheckType = InvCheckFilter.containsKey("inventoryCheckType") ? InvCheckFilter.getString("inventoryCheckType") : null;
        String inventoryCheckCategory = InvCheckFilter.containsKey("inventoryCheckCategory") ? InvCheckFilter.getString("inventoryCheckCategory") : null;
        String spmsStatus = InvCheckFilter.containsKey("spmsStatus") ? InvCheckFilter.getString("spmsStatus") : null;
        String inventoryCheckId = InvCheckFilter.containsKey("inventoryCheckId") ? InvCheckFilter.getString("inventoryCheckId") : null;
        String spmsId = InvCheckFilter.containsKey("spmsId") ? InvCheckFilter.getString("spmsId") : null;
        String startDate = null, endDate = null;
        if (InvCheckFilter.containsKey("createDate")) {
            List<String> datePeriod = (List) InvCheckFilter.getJSONArray("createDate");
            if (datePeriod.size() == 2) {
                startDate = datePeriod.get(0);
                endDate = datePeriod.get(1);
            }
        }
        QueryWrapper<InventoryCheckAction> queryWrapper = new QueryWrapper<InventoryCheckAction>();
        if (null != werks && werks.size()>0) {
            queryWrapper.in("WERKS", werks);
        }
        if (null != whsLocationCode && whsLocationCode.size()>0) {
            queryWrapper.in("WHS_LOCATION_CODE", whsLocationCode);
        }
        if (StringUtils.isNotEmpty(inventoryCheckType)) {
            queryWrapper.eq("INVENTORY_CHECK_TYPE", inventoryCheckType);
        }
        if (StringUtils.isNotEmpty(inventoryCheckCategory)) {
            queryWrapper.eq("INVENTORY_CHECK_CATEGORY", inventoryCheckCategory);
        }
        if (StringUtils.isNotEmpty(inventoryCheckId)) {
            queryWrapper.eq("INVENTORY_CHECK_ID", inventoryCheckId);
        }
        if (StringUtils.isNotEmpty(spmsId)) {
            queryWrapper.eq("SPMS_ID", spmsId);
        }
        if (StringUtils.isNotEmpty(spmsStatus)) {
            queryWrapper.eq("SPMS_STATUS", spmsStatus);
        }
        if (StringUtils.isNotEmpty(startDate)) {
            queryWrapper.between("CREATE_DATE", startDate, endDate);
        }
        try {
            return ResponseResult.success().add("invCheckAcList", this.baseMapper.selectList(queryWrapper.orderByDesc("CREATE_TIME")));
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error("Failed when query Inventory Check Action");
        }
    }

    /**新建盘盈亏单据
     * 1. 有uuid 更新
     * 2. 无uuid 新建
     * */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult updateInvCheckAc(JSONObject InvCheckDoc, String userId) {
        try{
            JSONObject invCheckAc=InvCheckDoc.containsKey("invCheckAc")?InvCheckDoc.getJSONObject("invCheckAc"):null;
            JSONArray invCheckAcDtls=InvCheckDoc.containsKey("invCheckAcDtls")?InvCheckDoc.getJSONArray("invCheckAcDtls"):null;
            if(null==invCheckAc || null==invCheckAcDtls){
               throw new Exception("Error, invCheckAc or invCheckAcDtls missed from the parameters, please double check");
            }
            InventoryCheckAction inventoryCheckAction=invCheckAc.toJavaObject(InventoryCheckAction.class);
            if(StringUtils.isNotEmpty(inventoryCheckAction.getUuid())){
                ResponseResult checkStatusValidateResp = checkStatusValidate(invCheckAc);
                if (200 != checkStatusValidateResp.getCode()){
                    return checkStatusValidateResp;
                }
                // 更新
                QueryWrapper<InventoryCheckAction> queryWrapper=new QueryWrapper<InventoryCheckAction>().eq("UUID",inventoryCheckAction.getUuid());
                ResponseResult res= iInventoryCheckActionDtlService.updateInvCheckDtl(inventoryCheckAction.getUuid(),invCheckAcDtls);
                if(res.getCode()==200){
                    this.baseMapper.update(inventoryCheckAction,queryWrapper);
                    iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(inventoryCheckAction.getUuid(), userId,
                            "701".equals(inventoryCheckAction.getInventoryActionType()) ? INV_CHECK_ACTION_PY_UPDATE : INV_CHECK_ACTION_PK_UPDATE);
                    return res.add("invCheckAc",this.baseMapper.selectOne(queryWrapper));
                }
               throw new Exception(res.getMsg());
            }else{
                // 新建
                String uuid = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
                inventoryCheckAction.setUuid(uuid);
                ResponseResult res=iInventoryCheckActionDtlService.updateInvCheckDtl(uuid,invCheckAcDtls);
                if(res.getCode()==200){
                    this.baseMapper.insert(inventoryCheckAction);
                    iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(uuid, userId,
                            "701".equals(inventoryCheckAction.getInventoryActionType()) ? INV_CHECK_ACTION_PY : INV_CHECK_ACTION_PK);
                    QueryWrapper<InventoryCheckAction> queryWrapper=new QueryWrapper<InventoryCheckAction>().eq("UUID",uuid);
                    return res.add("invCheckAc",this.baseMapper.selectOne(queryWrapper));
                }
                throw new Exception(res.getMsg());
            }
        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    /** 更新之前检验当前数据状态是否和 currentStatus 一致，不一致要防止重复提交导致的更新操作 */
    ResponseResult checkStatusValidate(JSONObject invCheckAc){
        try {
            String uuid = invCheckAc.getString("uuid");
            String currentStatus = invCheckAc.getString("currentStatus");
            if (StringUtils.isEmpty(uuid) || StringUtils.isEmpty(currentStatus)){
                return ResponseResult.error().add("error", "更新单据uuid 或 currentStatus不能为空");
            }
            InventoryCheckAction checkAction = this.baseMapper.selectOne(new QueryWrapper<InventoryCheckAction>().eq("UUID", uuid));
            if (checkAction == null || !currentStatus.equals(checkAction.getSpmsStatus())){
                return ResponseResult.error().add("error", "单据状态已更新请刷新后重试");
            }
            return ResponseResult.success();
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    /** 根据任务uuid，获取盘盈盘亏结果提交SAP；状态为 1草稿，6提交SAP失败 才能被提交，其余状态返回错误信息。
     * 1.先检查数据是否都可操作；
     * 2.计算仓库库存，计算储位库存，记录仓库库存、储位库存操作日志；
     * 3.提交SAP成功，更新单据状态为 4提交SAP成功，记录返回信息，记录日志；提交SAP失败，更新状态为6提交SAP失败，记录日志；
     * 4.重新提交SAP时，不执行库存计算过程。
     * */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult getInventoryCheckActionToSap(String uuid, String postingDate, String userId, String remarks, String currentStatus) {
        if (StringUtils.isEmpty(postingDate) || StringUtils.isEmpty(uuid)){
            return ResponseResult.error().add("error", "请求参数错误请重试");
        }
        InventoryCheckAction inventoryCheckAction = this.baseMapper.selectOne(new QueryWrapper<InventoryCheckAction>().eq("UUID", uuid));
        if (inventoryCheckAction == null){
            return ResponseResult.error().add("error", "此盘盈盘亏单据不存在");
        }
        if (!currentStatus.equals(inventoryCheckAction.getSpmsStatus())){
            return ResponseResult.error().add("error", "单据状态已更新请刷新后重试");
        }
        if (!"1".equals(inventoryCheckAction.getSpmsStatus()) && !"6".equals(inventoryCheckAction.getSpmsStatus())){
            return ResponseResult.error().add("error", "此状态禁止提交SAP");
        }
        Map<String, Object> Head = formatHeadData(inventoryCheckAction, postingDate);
        List<Map<String, Object>> Items = new ArrayList<>();
        if ("M".equals(inventoryCheckAction.getInventoryCheckType())){
            Items  = formatItemsDataM(uuid);
        }else {
            Items = formatItemsDataX(uuid);
        }
        if (Head == null || Items.size() == 0){
            return ResponseResult.error();
        }
        /** 合规数据提交SAP，先更新SPMS仓库库存、储位库存，更新成功后再提交SAP；更新失败返回错误信息。
         * 4-提交SAP成功，获取提交返回结果；6-提交SAP失败；
         */
        try {
            /** 首次提交SAP时计算库存；提交SAP失败单据重新提交SAP忽略库存计算。 */
            if ("1".equals(inventoryCheckAction.getSpmsStatus())){
                ResponseResult updateInventoryResult = updateInventory(inventoryCheckAction, uuid, userId);
                if (updateInventoryResult.getCode() != 200){
                    throw new Exception("库存更新过程出错");
                    //return updateInventoryResult;
                }
            }
            JSONObject dataToSap = formatDataToSap(Head, Items);
            JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));

            if (message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").containsKey("O_TYPE")
                    && "S".equals(message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").getString("O_TYPE"))){

                String O_MBLNR = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").getString("O_MBLNR").trim();
                String O_MJAHR = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").getString("O_MJAHR").trim();
                this.baseMapper.updateInventoryCheckActionStatus(uuid, "4", O_MBLNR, O_MJAHR, postingDate, remarks);
                iSapDeliveryNoteLogService.insertSapRequestRecord(uuid, userId,
                        "701".equals(inventoryCheckAction.getInventoryActionType()) ? INV_CHECK_ACTION_PY_SAP : INV_CHECK_ACTION_PK_SAP, message.toJSONString(), dataToSap.toJSONString());
                return ResponseResult.success()
                        .add("sapReturnedMessage", message)
                        .add("inventoryCheckAction", this.baseMapper.selectOne(new QueryWrapper<InventoryCheckAction>().eq("UUID", uuid)))
                        .add("inventoryCheckActionDtl", iInventoryCheckActionDtlService.getICADList(uuid))
                        .add("dataToSap", dataToSap);
            }
            this.baseMapper.updateInventoryCheckActionStatus(uuid, "6", "", "", postingDate, remarks);
            iSapDeliveryNoteLogService.insertSapRequestRecord(uuid, userId,
                    "701".equals(inventoryCheckAction.getInventoryActionType()) ? INV_CHECK_ACTION_PY_SAP_ERROR : INV_CHECK_ACTION_PK_SAP_ERROR, message.toJSONString(), dataToSap.toJSONString());
            return ResponseResult.error().add("sapReturnedMessage", message).add("dataToSap", dataToSap);

        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("请求ESB出错");
        }
    }

    /**
     * 701 盘盈，计算备件货仓库库存、储位库存；计算贸易货储位库存；
     * 702 盘亏，计算备件货仓库库存、储位库存；计算贸易货储位库存；
     * @param inventoryCheckAction
     * @param uuid
     * @return
     */
    ResponseResult updateInventory(InventoryCheckAction inventoryCheckAction, String uuid, String userId){
        try {
            List<Map<String, Object>> inventoryDtl = this.baseMapper.getInventoryCheckActionAndDtl(uuid);
            ResponseResult checkResult = checkDataValidate(inventoryCheckAction, inventoryDtl);
            if (checkResult.getCode() != 200){
                return checkResult;
            }
            for (int i = 0; i < inventoryDtl.size(); i++){
                String materialNo = inventoryDtl.get(i).get("materialNo") == null ? null : inventoryDtl.get(i).get("materialNo").toString();
                String boxNote = inventoryDtl.get(i).get("boxNote") == null ? null : inventoryDtl.get(i).get("boxNote").toString();
                double discrepancy = Double.parseDouble(inventoryDtl.get(i).get("discrepancy").toString());
                String storageUuid = inventoryDtl.get(i).get("storageUuid").toString();
                int result = iWhsStorageInventoryService.updateWhsStorageInventory(inventoryCheckAction, materialNo, boxNote, discrepancy, storageUuid, userId);
                if (result < 0){
                    return ResponseResult.error("库存更新出错");
                }
            }
            return ResponseResult.success();
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.error();
        }
    }

    ResponseResult checkDataValidate(InventoryCheckAction inventoryCheckAction, List<Map<String, Object>> inventoryDtl){
        try {
            /** 701-盘盈加库存，可忽略判断；702-盘亏需要扣减库存，需检查对应库存是否够扣减。 */
            if ("702".equals(inventoryCheckAction.getInventoryActionType())){
                if (inventoryDtl == null || inventoryDtl.size() == 0){
                    return ResponseResult.error("请求盘盈盘亏结果查询不存在");
                }
                for (int i = 0; i < inventoryDtl.size(); i ++){
                    String storageUuid = inventoryDtl.get(i).get("storageUuid").toString();
                    if (storageUuid == null){
                        return ResponseResult.error("盘点结果信息对应储位信息不能为空");
                    }
                    double discrepancy = Double.parseDouble(inventoryDtl.get(i).get("discrepancy").toString());
                    if ("M".equals(inventoryCheckAction.getInventoryCheckType())){
                        String materialNo = inventoryDtl.get(i).get("materialNo").toString();
                        WhsStorageInventory materialOnStorage = iWhsStorageInventoryService.getStorageMaterialNo(storageUuid, materialNo);
                        if (materialOnStorage == null || discrepancy > materialOnStorage.getAvailableQty()){
                            return ResponseResult.error(materialNo + " 盘亏数量大于对应储位库存数量，请检查数据后重试");
                        }
                    }else {
                        String boxNote = inventoryDtl.get(i).get("boxNote").toString();
                        WhsStorageInventory materialOnStorage = iWhsStorageInventoryService.getStorageBoxNote(storageUuid, boxNote);
                        if (materialOnStorage == null || discrepancy > materialOnStorage.getAvailableQty()){
                            return ResponseResult.error(boxNote + " 盘亏数量大于对应储位库存数量，请检查数据后重试");
                        }
                    }
                }
            }
            return ResponseResult.success();
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.error("库存校验出错");
        }
    }

    JSONObject formatDataToSap(Map<String, Object> Head, List<Map<String, Object>> Items){
        JSONObject object = new JSONObject();
        JSONObject ESB_ATTRS = new JSONObject();
        JSONObject REQUEST_DATA = new JSONObject();
        JSONObject REQUEST = new JSONObject();

        ESB_ATTRS.put("App_ID", "SPMS");
        ESB_ATTRS.put("Application_ID", "00020000000002");
        ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());

        REQUEST_DATA.put("Head", Head);
        REQUEST_DATA.put("Items", Items);
        REQUEST_DATA.put("Operation", "Z_SPMS_SKU_CHECK");
        REQUEST_DATA.put("Type", "SKU_CHECK");

        REQUEST.put("REQUEST_DATA", REQUEST_DATA);
        REQUEST.put("ESB_ATTRS", ESB_ATTRS);
        object.put("REQUEST", REQUEST);
        return object;
    }

    Map<String, Object> formatHeadData(InventoryCheckAction inventoryCheckAction, String postingDate){
        Map<String, Object> map = new HashMap<>();
        map.put("bldat", inventoryCheckAction.getCreateDate().replaceAll("-", ""));
        map.put("budat", postingDate.replaceAll("-", ""));
        map.put("bwart", inventoryCheckAction.getInventoryActionType());
        map.put("werks", inventoryCheckAction.getWerks());
        map.put("lgort", inventoryCheckAction.getWhsLocationCode());
        map.put("bktxt", inventoryCheckAction.getVoucherText() == null ? "" : inventoryCheckAction.getVoucherText());
        return map;
    }

    List<Map<String, Object>> formatItemsDataM(String uuid){
        try {
            List<Map<String, Object>> checkActionDtlList = iInventoryCheckActionDtlService.getInventoryActionDtlM(uuid);
            if (checkActionDtlList.size() == 0){
                return null;
            }
            List<Map<String, Object>> mapList = new ArrayList<>();
            for (Map<String, Object> obj : checkActionDtlList){
                Map<String, Object> map = new HashMap<>();
                map.put("zeile", obj.get("taskRowId").toString());
                map.put("matnr", obj.get("materialNo").toString());
                map.put("menge", obj.get("discrepancy").toString());
                map.put("meins", obj.get("basicUnit").toString());
                map.put("lifnr", obj.get("relateAccount") == null ? "" : obj.get("relateAccount").toString());
                map.put("kostl", obj.get("costCenter") == null ? "" : obj.get("costCenter").toString());
                map.put("sgtxt", obj.get("voucherItemText") == null ? "" : obj.get("voucherItemText").toString());
                map.put("werks", obj.get("werks").toString());
                map.put("lgort", obj.get("whsLocationCode").toString());
                mapList.add(map);
            }
            return mapList;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    List<Map<String, Object>> formatItemsDataX(String uuid){
        try {
            List<Map<String, Object>> checkActionDtlList = iInventoryCheckActionDtlService.getInventoryActionDtlX(uuid);
            if (checkActionDtlList.size() == 0){
                return null;
            }
            List<Map<String, Object>> mapList = new ArrayList<>();
            for (Map<String, Object> obj : checkActionDtlList){
                Map<String, Object> map = new HashMap<>();
                map.put("zeile", obj.get("rowNum").toString());
                map.put("matnr", obj.get("materialNo").toString());
                map.put("menge", obj.get("quantity").toString());
                map.put("meins", obj.get("basicUnit").toString());
                map.put("lifnr", obj.get("relateAccount") == null ? "" : obj.get("relateAccount").toString());
                map.put("kostl", obj.get("costCenter") == null ? "" : obj.get("costCenter").toString());
                map.put("sgtxt", obj.get("voucherItemText") == null ? "" : obj.get("voucherItemText").toString());
                map.put("werks", obj.get("werks").toString());
                map.put("lgort", obj.get("whsLocationCode").toString());
                mapList.add(map);
            }
            return mapList;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public List<Map<String, Object>> getICALogs(String uuid) {
        return iSapDeliveryNoteLogService.getDocumentLogs(uuid);
    }
}
